package grape.workflow.rest.form.mvc;

import grape.common.exception.runtime.BadRequestException;
import grape.common.rest.mvc.SuperController;
import grape.workflow.rest.form.FormTypeHelper;
import grape.workflow.rest.form.form.FormListForm;
import grape.workflow.rest.form.mapper.FormWebMapper;
import grape.workflow.rest.form.vo.DynamicFormVo;
import grape.workflow.rest.form.vo.FormTypeVo;
import grape.workflow.rest.form.vo.RelationFormVo;
import grape.workflow.service.form.enums.FormType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.common.engine.impl.el.ExpressionManager;
import org.flowable.engine.*;
import org.flowable.engine.form.AbstractFormType;
import org.flowable.engine.form.FormData;
import org.flowable.engine.form.FormProperty;
import org.flowable.engine.history.HistoricDetail;
import org.flowable.engine.impl.form.FormPropertyHandler;
import org.flowable.engine.impl.form.FormPropertyImpl;
import org.flowable.engine.impl.form.FormTypes;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.HistoricFormPropertyEntityImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.form.api.FormInfo;
import org.flowable.form.api.FormRepositoryService;
import org.flowable.form.model.FormField;
import org.flowable.form.model.OptionFormField;
import org.flowable.form.model.SimpleFormModel;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.task.api.TaskInfo;
import org.flowable.ui.common.service.exception.NotFoundException;
import org.flowable.variable.service.impl.el.NoExecutionVariableScope;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created by yangwei
 * Created at 2020/7/1 17:20
 */
@RestController
@RequestMapping("/workflow/form")
@Api(tags = "表单相关接口")
public class FormController extends SuperController {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private FormTypeHelper formTypeHelper;
    @Autowired
    private FormService formService;
    @Autowired
    private FormWebMapper formWebMapper;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    protected FormRepositoryService formRepositoryService;
    @Autowired
    private SpringProcessEngineConfiguration springProcessEngineConfiguration;

    @ApiOperation("流程启动或任务动态表单")
    @PreAuthorize("hasAuthority('user')")
    @GetMapping(value = "/dynamicForm")
    @ResponseStatus(HttpStatus.OK)
    public List<DynamicFormVo> dynamicForm(@Valid FormListForm dynamicListForm){
        FormData formData = null;
        if(dynamicListForm.getIsStartForm()){
            // 启动表单
            ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
            if (!isStrEmpty(dynamicListForm.getProcessDefinitionId())) {
                processDefinitionQuery.processDefinitionId(dynamicListForm.getProcessDefinitionId());
            }
            if (!isStrEmpty(dynamicListForm.getProcessDefinitionKey())) {
                processDefinitionQuery.processDefinitionKey(dynamicListForm.getProcessDefinitionKey()).latestVersion();
            }
            ProcessDefinition definition = processDefinitionQuery.singleResult();
            formData = formService.getStartFormData(definition.getId());
        }else {
            TaskInfo task = taskService.createTaskQuery().taskId(dynamicListForm.getTaskId()).singleResult();
            boolean isRuntimeTask = true;
            if (task == null) {
                task = historyService.createHistoricTaskInstanceQuery().taskId(dynamicListForm.getTaskId()).singleResult();
                isRuntimeTask = false;
            }
            // 历史任务表单这里查询不到，
            if (isRuntimeTask) {
                formData = formService.getTaskFormData(dynamicListForm.getTaskId());
            }else{
                // 历史任务动态表单没有找到合适的方法，这里自己解析一下

                List<org.flowable.bpmn.model.FormProperty> formProperties = formTypeHelper.taskModelFormProperties(task);
                if(isEmpty(formProperties)){
                    throw new BadRequestException("没有找到动态表单数据定义");
                }
                List<HistoricDetail> list = historyService.createHistoricDetailQuery().taskId(dynamicListForm.getTaskId()).formProperties().list();
                return formProperties.stream().map(formProperty -> {
                    HistoricDetail historicDetail1 = list.stream().filter(historicDetail -> (((HistoricFormPropertyEntityImpl) historicDetail).getPropertyId().equals(formProperty.getId()))).findFirst().orElse(null);
                    return formWebMapper.formPropertyToVo(formProperty, ((HistoricFormPropertyEntityImpl) historicDetail1));
                }).collect(Collectors.toList());
            }

        }
        if (formData == null) {
            throw new BadRequestException("没有找到动态表单数据定义");
        }
        List<FormProperty> formProperties = formData.getFormProperties();

        return formProperties.stream().map(formWebMapper::formPropertyToVo).collect(Collectors.toList());

    }

    @ApiOperation("流程启动或任务外置表单")
    @PreAuthorize("hasAuthority('user')")
    @GetMapping(value = "/relationForm")
    @ResponseStatus(HttpStatus.OK)
    public List<RelationFormVo> relationForm(FormListForm relationListForm){
        FormInfo formInfo = null;
        if(relationListForm.getIsStartForm()){
            // 启动表单
            ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
            if (!isStrEmpty(relationListForm.getProcessDefinitionId())) {
                processDefinitionQuery.processDefinitionId(relationListForm.getProcessDefinitionId());
            }
            if (!isStrEmpty(relationListForm.getProcessDefinitionKey())) {
                processDefinitionQuery.processDefinitionKey(relationListForm.getProcessDefinitionKey()).latestVersion();
            }
            ProcessDefinition definition = processDefinitionQuery.singleResult();

            try {
                formInfo = formTypeHelper.getStartFormDependFormKey(definition);
            } catch (NotFoundException | FlowableObjectNotFoundException ne) {
                if (formInfo == null) {
                    throw new BadRequestException("没有找到外置表单数据定义");
                }
            }
        }else {
            formInfo = taskService.getTaskFormModel(relationListForm.getTaskId());
        }
        if (formInfo == null) {
            throw new BadRequestException("没有找到外置表单数据定义");
        }

        // 查看源码目前FormModel就一个实现
        SimpleFormModel formModel = (SimpleFormModel) formInfo.getFormModel();
        List<FormField> fields = formModel.getFields();
        return fields.stream().map(item->{
            RelationFormVo relationFormVo = formWebMapper.formFieldToVo(item);
            OptionFormField optionFormField = item instanceof OptionFormField ? ((OptionFormField) item) : null;
            if (optionFormField != null) {
                relationFormVo.setOptions(optionFormField.getOptions());
            }
            return relationFormVo;
        })
                .collect(Collectors.toList());
    }

    @ApiOperation("任务的表单类型")
    @PreAuthorize("hasAuthority('user')")
    @GetMapping(value = "/task/{taskId}/taskFormType")
    @ResponseStatus(HttpStatus.OK)
    public FormTypeVo taskFormType(@PathVariable String taskId) {
        return formTypeHelper.getTaskFormTypeVo(taskId);
    }

}
